package com.letu.aopcache.util;

import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.letu.aopcache.model.CacheWrapper;
import com.letu.aopcache.model.MethodMsg;
import com.letu.core.base.R;
import com.letu.core.dynamic.DynamicAnno;
import com.letu.core.dynamic.DynamicData;
import com.letu.core.dynamic.FieldType;
import com.letu.core.dynamic.FieldTypeUtil;
import com.letu.core.model.CacheKeyBuilder;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

/**
 * 缓存工具类
 */
@Slf4j
public class CacheUtil {

    private static final String SPLIT_STR = "_";

    @SuppressWarnings("rawtypes")
    public static boolean isEmpty(Object obj) {
        if (null == obj) {
            return true;
        }
        if (obj instanceof String) {
            return ((String) obj).length() == 0;
        }
        Class cl = obj.getClass();
        if (cl.isArray()) {
            int len = Array.getLength(obj);
            return len == 0;
        }
        if (obj instanceof Collection) {
            Collection tempCol = (Collection) obj;
            return tempCol.isEmpty();
        }
        if (obj instanceof Map) {
            Map tempMap = (Map) obj;
            return tempMap.isEmpty();
        }
        return false;
    }

    public static Map<String, Object> assembleIds(Object obj) {
        Map<String, Object> map = new HashMap<>();
        if (isEmpty(obj)) {
            return null;
        }
        List listObject;
        List<Long> ids = new ArrayList<>();
        if (obj instanceof R) {
            R result = (R) obj;
            listObject = findList(result.getData());
        } else {
            listObject = findList(obj);
        }
        map.put("ids", findIds(listObject));
        map.put("listObject", listObject);
        String name = listObject.get(0).getClass().getName();
        name = name.replaceAll("\\.", "");
        map.put("objectClass",name );
        return map;
    }

    public static List findList(Object object) {
        if (isEmpty(object)) {
            return null;
        }
        List listObject = new ArrayList();
        if (IPage.class.getName().equals(object.getClass().getName())) {
            IPage pageData = (IPage) object;
            listObject = pageData.getRecords();

        } else if (Page.class.getName().equals(object.getClass().getName())) {
            Page pageData = (Page) object;
            listObject = pageData.getRecords();
        } else if (FieldType.ARRAYLIST.getClazz().getName().equals(object.getClass().getName()) ||
                FieldType.ARRAYLIST.getTargerClazz().getName().equals(object.getClass().getName())) {
            if (object != null) {
                listObject = (List) object;
            }

        } else if (FieldType.SET.getTargerClazz().getName().equals(object.getClass().getName())) {
            if (object != null) {
                Set set = (Set) object;
                List list2 = new ArrayList<String>();
                list2.addAll(set);
                listObject = list2;
            }
        } else {
            listObject.add(object);
        }
        return listObject;
    }

    /**
     * 组装结果数据
     *
     * @param cacheWrapper
     * @param listData
     * @return
     */
    public static Object assembleResultData(CacheWrapper<Object> cacheWrapper, List listData) {
        if (isEmpty(cacheWrapper)) {
            return null;
        }
        Object cacheObject = cacheWrapper.getCacheObject();
        //真实数据结构
        Object resultObject = cacheObject;
        if (cacheObject instanceof R) {
            R result = (R) cacheObject;
            resultObject = result.getData();
        }
        if (IPage.class.getName().equals(resultObject.getClass().getName())) {
            IPage pageData = (IPage) resultObject;
            pageData.setRecords((List) listData);
            if (cacheObject instanceof R) {
                return R.success(pageData);
            }
            return pageData;

        } else if (Page.class.getName().equals(resultObject.getClass().getName())) {
            Page pageData = (Page) resultObject;
            pageData.setRecords((List) listData);
            if (cacheObject instanceof R) {
                return R.success(pageData);
            }
            return pageData;
        } else if (FieldType.ARRAYLIST.getClazz().getName().equals(resultObject.getClass().getName()) ||
                FieldType.ARRAYLIST.getTargerClazz().getName().equals(resultObject.getClass().getName())) {
            if (cacheObject instanceof R) {
                return R.success(listData);
            }
            return listData;

        } else if (FieldType.SET.getTargerClazz().getName().equals(resultObject.getClass().getName())) {
            if (cacheObject instanceof R) {
                return R.success(listData);
            }
            return listData;
        } else if (cacheObject instanceof R) {
            return R.success(listData.get(0));
        }
        return listData.get(0);
    }

//    /**
//     * 返回类型是否是集合
//     * @param returnType
//     */
//    public static boolean isReturnTypeList(Class<?> returnType){
//        cacheWrapper.getReturnType()
//    }

    public static Object assembleInfaceData(Object object) {
        if (isEmpty(object)) {
            return null;
        }
        //将R 剥离出来
        Object dataObject = object;
        if (object instanceof R) {
            R result = (R) object;
            dataObject = result.getData();
        }

        if (IPage.class.getName().equals(dataObject.getClass().getName())) {
            IPage pageData = (IPage) dataObject;
            pageData.setRecords(null);
            if (object instanceof R) {
                return R.success(pageData);
            }
            return pageData;
        } else if (Page.class.getName().equals(dataObject.getClass().getName())) {
            Page pageData = (Page) dataObject;
            pageData.setRecords(null);
            if (object instanceof R) {
                return R.success(pageData);
            }
            return pageData;
        } else if (FieldType.ARRAYLIST.getClazz().getName().equals(dataObject.getClass().getName()) ||
                FieldType.ARRAYLIST.getTargerClazz().getName().equals(dataObject.getClass().getName())) {
            if (object instanceof R) {
                return R.success(new ArrayList<>());
            }
            return new ArrayList<>();
        } else if (FieldType.SET.getTargerClazz().getName().equals(dataObject.getClass().getName())) {
            if (object instanceof R) {
                return R.success(new HashSet<>());
            }
            return new HashSet<>();
        }
        return object;
    }

    public Object getDataType(Object resultData) {
        if (FieldType.ARRAYLIST.getClazz().getName().equals(resultData.getClass().getName()) ||
                FieldType.ARRAYLIST.getTargerClazz().getName().equals(resultData.getClass().getName())) {
            return new ArrayList<>();
        } else if (FieldType.SET.getTargerClazz().getName().equals(resultData.getClass().getName())) {
            return new HashSet<>();
        }
        return null;
    }


    public static List<Long> findIds(List listObject) {
        List<Long> ids = new ArrayList<>();
        for (Object object : listObject) {
            Long l = getIdInvok(object);
            if (l == null) {
                return null;
            }
            ids.add(l);
        }
        return ids;
    }


    /**
     * 获取排序orderid
     *
     * @param object
     * @return
     */
    public static Long getIdInvok(Object object) {
        Method m = null;
        try {
            m = object.getClass().getMethod("getId");
            Object pointObj = m.invoke(object);
            return (Long) pointObj;
        } catch (Exception e) {
            log.info("invok  主键id失败。说明不是实体，则不缓存");
        }
        return null;
    }


    /**
     * 获取全量缓存key
     *
     * @param methodMsg
     * @return
     */
    public static CacheKeyBuilder getCacheKeyInvok(MethodMsg methodMsg) throws Exception {
        Class clazz = methodMsg.getTarget().getClass();
        Method gcache = clazz.getMethod("getCacheKeyBuilder");
        CacheKeyBuilder cacheKeyBuilder = (CacheKeyBuilder) gcache.invoke(methodMsg.getTarget(), null);//通过getter方法返回值
        return cacheKeyBuilder;
    }


    /**
     * 生成字符串的HashCode
     *
     * @param buf
     * @return int hashCode
     */
    private static int getHashCode(String buf) {
        int hash = 5381;
        int len = buf.length();

        while (len-- > 0) {
            hash = ((hash << 5) + hash) + buf.charAt(len);
        }
        return hash;
    }

    /**
     * 将Object 对象转换为唯一的Hash字符串
     *
     * @param obj Object
     * @return Hash字符串
     */
    public static String getUniqueHashStr(Object obj) {
        String s = BeanUtil.toString(obj);
        return getMiscHashCode(s);
    }

    /**
     * 通过混合Hash算法，将长字符串转为短字符串（字符串长度小于等于20时，不做处理）
     *
     * @param str String
     * @return Hash字符串
     */
    public static String getMiscHashCode(String str) {
        if (null == str || str.length() == 0) {
            return "";
        }
        int originSize = 20;
        if (str.length() <= originSize) {
            return str;
        }
        StringBuilder tmp = new StringBuilder();
        tmp.append(str.hashCode()).append(SPLIT_STR).append(getHashCode(str));

        int mid = str.length() / 2;
        String str1 = str.substring(0, mid);
        String str2 = str.substring(mid);
        tmp.append(SPLIT_STR).append(str1.hashCode());
        tmp.append(SPLIT_STR).append(str2.hashCode());

        return tmp.toString();
    }

    /**
     * 生成缓存Key
     *
     * @param className 类名称
     * @param method    方法名称
     * @param arguments 参数
     * @param header    header头信息
     * @return CacheKey 缓存Key
     */
    public static String getDefaultCacheKey(String className, String method, Object[] arguments, Map<String, Object> header) {
        StringBuilder sb = new StringBuilder();
        sb.append(getDefaultCacheKeyPrefix(className, method, arguments));
        if (null != arguments && arguments.length > 0) {
//            log.info("arguments is {}",JSONObject.toJSONString(arguments));
//            log.info("header is {}",JSONObject.toJSONString(header));
            Object[] objects = new Object[]{arguments, header};
            sb.append(getUniqueHashStr(objects));
        }
        return sb.toString();
    }

    /**
     * 生成缓存Key的前缀
     *
     * @param className 类名称
     * @param method    方法名称
     * @param arguments 参数
     * @return CacheKey 缓存Key
     */
    public static String getDefaultCacheKeyPrefix(String className, String method, Object[] arguments) {
        StringBuilder sb = new StringBuilder();
        sb.append(className);
        if (null != method && method.length() > 0) {
            sb.append(".").append(method);
        }
        return sb.toString();
    }

}
